/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: lwgpio_mpc5125.c$
* $Version : 3.8.4.1$
* $Date    : Feb-17-2012$
*
* Comments:
*
*   This file contains the GPIO standard functions used on boards
*
*END************************************************************************/

#include "mqx.h"
#include "bsp.h"
#include "lwgpio.h"

#define LWGPIO_PIN_FROM_ID(id)      (((id) & LWGPIO_PIN_MASK) >> LWGPIO_PIN_SHIFT)

#define LWGPIO_PORT_FROM_ID(id)     (((id) & LWGPIO_PORT_MASK) >> LWGPIO_PORT_SHIFT)

/* Interrupt mode definitions */
#define LWGPIO_INT_MODE_SHIFT       (11) 
#define LWGPIO_INT_MODE_MASK        (0x0F << LWGPIO_INT_MODE_SHIFT)
#define LWGPIO_INT_MODE(x)          (((x) << LWGPIO_INT_MODE_SHIFT) & LWGPIO_INT_MODE_MASK)
#define LWGPIO_INT_MODE_FROM_ID(id) (((id) & LWGPIO_INT_MODE_MASK) >> LWGPIO_INT_MODE_SHIFT)

/* Direction field definitions */
#define LWGPIO_DIR_SHIFT            (20)
#define LWGPIO_DIR_MASK             (0x01 << LWGPIO_DIR_SHIFT)
#define LWGPIO_FUNCTION_SHIFT       (0x05) 

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Decodes ID to HW specific struct and then performs pin initialization
*
*END*********************************************************************/
boolean lwgpio_init
(
    /* Pointer to LWGPIO internal structure to be filled in */
    LWGPIO_STRUCT_PTR handle,
    /* Pin ID, bitmask integer value */
    LWGPIO_PIN_ID     id,
    /* Direction to be set within initialization */
    LWGPIO_DIR        dir,
    /* Value to be set within initialization */
    LWGPIO_VALUE      value
)
{ /* Body */
    uint_32       port_idx, pin_idx,temp;
    port_idx = LWGPIO_PORT_FROM_ID(id);
    pin_idx =  LWGPIO_PIN_FROM_ID(id);
    
    if (port_idx==0)/*GPIO port 0*/
    {
        if ((0 <= pin_idx)&&(pin_idx <= 3))
        {
            if (dir == LWGPIO_DIR_OUTPUT)
            {
                return FALSE;
            }
            temp= pin_idx;
        }
        else if ((4 <= pin_idx)&&(pin_idx <= 8))
        {
            /*Address IO control register (control GPIO pin from 4 to 8) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx*/
            temp= pin_idx;
        }
        else if ((9 <= pin_idx)&&(pin_idx <= 20))
        {
            /*Address IO control register (control GPIO pin from 9 to 20) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx +4*/
            temp= pin_idx + 4;
        }  
        else if ((21 <= pin_idx)&&(pin_idx <= 28))
        {
            /*Address IO control register (control GPIO pin from 21 to 28) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 8*/
            temp= pin_idx + 8;
        }  
        else if ((29 <= pin_idx)&&(pin_idx <= 30))
        {
            /*Address IO control register (control GPIO pin from 29 to 30) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 16*/
            temp= pin_idx + 16;
        }  
        else if ( pin_idx == 31)
        {
            /*Address IO control register (control GPIO pin 31) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx +19*/
            temp= pin_idx + 19;
        }
    }
    else if(port_idx==1) /*GPIO port 1*/
    {
        if ((0 <= pin_idx)&&(pin_idx <= 1))
        {
            /*Address IO control register (control GPIO pin from 0 to 1) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 51*/
            temp= pin_idx + 51;
        }    
        else if ((2 <= pin_idx)&&(pin_idx <= 8))
        {
            /*Address IO control register (control GPIO pin from 2 to 8) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 54*/
            temp= pin_idx + 54;
        }  
        else if ((9 <= pin_idx)&&(pin_idx <= 10))
        {
            /*Address IO control register (control GPIO pin from 9 to 10) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 58*/
            temp= pin_idx + 58;
        }  
        else if ((11 <= pin_idx)&&(pin_idx <= 31))
        {
            /*Address IO control register (control GPIO pin from 11 to 31) =  IOCONTROL_BASE(0xFF40_A000) + pin_idx + 62*/
            temp= pin_idx + 62;
        }
    }
    else
    {
        return FALSE;
    }

    handle->pcr_reg = (uint_8_ptr)(&((VMPC5125_STRUCT_PTR) MPC5125_GET_IMMR() )->IOC) + temp;
    handle->flags = id;
    handle->gpio_ptr = MPC5125_GPIO_ADDR(port_idx);
    handle->pinmask  = 1 << (31 - pin_idx);
    handle->port_idx = port_idx;
    handle->pin_idx  = pin_idx;
    
    /* Set value prior to set to output */
    if (value != LWGPIO_VALUE_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_VALUE enum */
        lwgpio_set_value(handle, value);
    }

    if (dir != LWGPIO_DIR_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_DIR enum */
        lwgpio_set_direction(handle, dir);
    }

    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_attribute
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Sets attributes
*
*END*********************************************************************/
boolean lwgpio_set_attribute
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle,
    /* PORT attribute */
    uint_32 attribute_id,
    /* Attribute value */
    uint_32 value
)
{
    switch(attribute_id)
    {
        case(LWGPIO_ATTR_OPEN_DRAIN):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                handle->gpio_ptr->ODR |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                handle->gpio_ptr->ODR &= ~(handle->pinmask);
            }
            break;
        }
        default:
        {
            return FALSE;
        }
    }
    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_functionality
* Returned Value   : void
* Comments         :
*    Sets functionality (peripheral mode) of the pin
*
*END*********************************************************************/
void lwgpio_set_functionality
(
    /* Pin handle to set function on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Function to be set (integer value) */
    uint_32            function
)
{ 
    *handle->pcr_reg= IO_CONTROL_FUNCTION_MASK;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_functionality
* Returned Value   : void
* Comments         :
*    Gets functionality (peripheral mode) of the pin
*
*END*********************************************************************/
uint_32 lwgpio_get_functionality
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return (*handle->pcr_reg & IO_CONTROL_FUNCTION_MASK) >> LWGPIO_FUNCTION_SHIFT;
}


/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_direction
* Returned Value   : void
* Comments         :
*    Sets direction of the pin
*
*END*********************************************************************/
void lwgpio_set_direction
(
    /* Pin handle to set direction on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_DIR         dir
)
{   
    if (dir == LWGPIO_DIR_INPUT) {
        handle->gpio_ptr->DIR &= ~handle->pinmask;
        handle->flags |= LWGPIO_DIR_MASK; /* mark the pin is input */
    }
    else if (dir == LWGPIO_DIR_OUTPUT) {
        handle->gpio_ptr->DIR |= handle->pinmask;
        handle->flags &= ~LWGPIO_DIR_MASK; /* mark the pin is output */
    }
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_value
* Returned Value   : void
* Comments         :
*    Sets value (output latch) of the pin
*
*END*********************************************************************/
void lwgpio_set_value
(
    /* Pin handle to set value on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_VALUE       out_value
)
{
    if (out_value == LWGPIO_VALUE_LOW) {
        handle->gpio_ptr->DAT &= ~handle->pinmask;
    }
    else if (out_value == LWGPIO_VALUE_HIGH) {
        handle->gpio_ptr->DAT |= handle->pinmask;
    }
    else { /* LWGPIO_VALUE_NOCHANGE or other value */
    }
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_toggle_value
* Returned Value   : void
* Comments         :
*    Toggles value of output latch of the pin
*
*END*********************************************************************/
void lwgpio_toggle_value
(
    /* Pin handle to toggle value on */
    LWGPIO_STRUCT_PTR  handle
)
{
    lwgpio_set_value(handle, lwgpio_get_value(handle) == LWGPIO_VALUE_HIGH ? LWGPIO_VALUE_LOW : LWGPIO_VALUE_HIGH);
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_value
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns value (output latch or read data) of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_value
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    /* if the pin is set to output */
    if (handle->gpio_ptr->DIR & handle->pinmask) /* alternative: (handle->flags & LWGPIO_DIR_MASK == 0) */
        return (handle->gpio_ptr->DAT & handle->pinmask) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
    else
        return (handle->gpio_ptr->DAT & handle->pinmask) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_raw
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns read level of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_raw
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return (handle->gpio_ptr->DAT & handle->pinmask) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Initializes pin to generate interrupt
*
*END*********************************************************************/
boolean lwgpio_int_init
(
    /* Pin handle to initialize interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Interrupt mode */
    LWGPIO_INT_MODE   mode
)
{ /* Body */

    uint_32                      icr_shift;
      
    if (mode > 4 && mode != LWGPIO_INT_MODE_LOW)
        return FALSE;

    if (mode) {
        /* Store mode value and use it when enabling interrupt */
        if (handle->pin_idx < 16) 
        {
           icr_shift = 15-handle->pin_idx;
           handle->gpio_ptr->ICR1 &= ~(3<<icr_shift);
           handle->gpio_ptr->ICR1 |= ((mode&3)<<icr_shift);         
        }
        else
        {
           icr_shift = 31-handle->pin_idx;
           handle->gpio_ptr->ICR2 &= ~(3<<icr_shift);
           handle->gpio_ptr->ICR2 |= ((mode&3)<<icr_shift); 
        }
    }
    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_enable
* Returned Value   : void
* Comments         :
*    Enables / disables interrupts for specified pin
*
*END*********************************************************************/
void lwgpio_int_enable
(
    /* Pin handle to enable interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Enable or disable interrupt? TRUE = enable */
    boolean           ena
)
{ /* Body */
    if (ena) {
        /* enable pin interrupts */
       handle->gpio_ptr->IER |=  handle->pinmask;/*clear flag*/
       handle->gpio_ptr->IMR |=  handle->pinmask; /* the IRQ mode flags are on the same place as PCR_IRQC */
       _mpc5125_enable_interrupt(lwgpio_int_get_vector(handle));
    }
    else {
        /* disable pin interrupts */
      handle->gpio_ptr->IMR &= ~handle->pinmask;
      _mpc5125_disable_interrupt(lwgpio_int_get_vector(handle));
    }
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_get_flag
* Returned Value   : TRUE if interrupt flag is set
* Comments         :
*    Checks if there is pending interrupt flag for specified pin
*
*END*********************************************************************/
boolean lwgpio_int_get_flag
(
    /* Pin handle to get interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{ /* Body */
   return (handle->gpio_ptr->IER  & handle->pinmask) ? TRUE : FALSE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_clear_flag
* Returned Value   : void
* Comments         :
*    Clears pending interrupt flag on peripheral
*
*END*********************************************************************/
void lwgpio_int_clear_flag
(
    /* Pin handle to clear interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{ /* Body */
    /* Clear interrupt flag. Note that if an interrupt was acknowledged by NVIC,
    it will generate interrupt anyway. Interrupts on NVIC are grouped for each
    port */
   handle->gpio_ptr->IER |= handle->pinmask;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _bsp_get_gpio_base_address
* Returned Value   : pointer to base of GPIO registers
* Comments         :
*    This function returns base address of GPIO related register space.
*
*END*********************************************************************/
uint_32 lwgpio_int_get_vector
(
    /* Pin handle to get vector of */
    LWGPIO_STRUCT_PTR  handle
)
{
    switch (handle->port_idx) 
    {
    case 0:return MPC5125_GPIO1_VECTOR ;
           break;
    case 1:return MPC5125_GPIO2_VECTOR ;
           break;
    }
}

/* EOF */
